package edu.pku.sei.transformation.testcase;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.xml.parsers.ParserConfigurationException;

import org.xml.sax.SAXException;

import edu.pku.sei.bridge.mdabase.adapter.ECoreLoader;
import edu.pku.sei.bridge.mdabase.adapter.EMFXMILoader;
import edu.pku.sei.mdabase.basictypes.PrimitiveType;
import edu.pku.sei.mdabase.infrastructure.metamodel.MetaClass;
import edu.pku.sei.mdabase.infrastructure.metamodel.MetaReference;
import edu.pku.sei.mdabase.infrastructure.model.MModelGraph;
import edu.pku.sei.mdabase.infrastructure.runtime.Context;
import edu.pku.sei.queryscript.parser.ScriptParser;
import edu.pku.sei.transformation.modelType.ModelType;
import edu.pku.sei.transformation.mtparser.Ast;
import edu.pku.sei.transformation.mtparser.IRuleSemanticsBody;
import edu.pku.sei.transformation.pattern.TPatternEdge;
import edu.pku.sei.transformation.pattern.TPatternGraph;
import edu.pku.sei.transformation.pattern.TPatternNode;
import edu.pku.sei.transformation.semantics.TransformationController;
import edu.pku.sei.transformation.serialization.EMFXMISaver;
import edu.pku.sei.transformation.serialization.TransformationLoader;
import edu.pku.sei.transformation.structure.Domain;
import edu.pku.sei.transformation.structure.Key;
import edu.pku.sei.transformation.structure.Mode;
import edu.pku.sei.transformation.structure.PatternDomain;
import edu.pku.sei.transformation.structure.Rule;
import edu.pku.sei.transformation.structure.RuleBasedEnvironment;
import edu.pku.sei.transformation.structure.Trace;
import edu.pku.sei.transformation.structure.Transformation;
import edu.pku.sei.transformation.structure.TypedModel;
import edu.pku.sei.transformation.visitor.MTControllerVisitor;
import edu.pku.sei.transformation.visitor.MTScriptParser;

public class MTParserTestCase
{
	private String scriptContent;
	private String targetModelFile;
	
	MTParserTestCase(String content)
	{
		scriptContent = content;
	}
	
	public String getContent()
	{
		return scriptContent;
	}
	
	public static void main (String args[]) throws IOException, ParserConfigurationException, SAXException
	{
		File file = new File("st.txt");
		//File file = new File("st_check.txt");
		InputStream input = new FileInputStream(file);
		long len = file.length();
		byte[] temp = new byte[(int) len];
		input.read(temp);
		String s = new String(temp);
		MTParserTestCase testcase = new MTParserTestCase(s);
		input.close();
		Ast ast = MTScriptParser.parse(testcase.getContent());
		assert(ast != null);
		System.out.println("Parsing done");
		
		testcase.init();
		testcase.run(ast);
	}
	
	private RuleBasedEnvironment env = new RuleBasedEnvironment();
	private Context base = new Context();
	private Trace trace = new Trace();
	
	public RuleBasedEnvironment getEnv()
	{
		if (env == null)
			env = new RuleBasedEnvironment();
		return env;
	}
	
	public Context getBaseContext()
	{
		return base;
	}
	
	public Trace getTrace()
	{
		return trace;
	}
	
	public void init() throws ParserConfigurationException, SAXException, IOException
	{
		TransformationLoader loader = new TransformationLoader();
		String transFilename = "UML2RDBMS.trans";
		File file = new File(transFilename);
		Transformation trans;
		trans = loader.load(file);
		env.setTransformation(trans);
		for (Rule rule : trans.getOwnedRules())
		{
			env.registRule(rule.getName(), rule);
		}
		base.setEnvironment(env);
		loadModels();
//		createKeys();
//		createRules();
		//TransformationCont
//		List<Domain> domains = new ArrayList<Domain>();
//		for (PatternDomain domain : trans.getRule("ClassToTable").getDomains())
//		{
//			domains.add((Domain)domain);
//		}
	}
	
	public void run(Ast ast)
	{
		MTControllerVisitor visitor = new MTControllerVisitor(getEnv(), getBaseContext(), getTrace());
		ast.accept(visitor);
		env.setMTVisitor(visitor);
		System.out.println("First visit done");
		//first visit done, semantics loaded
		
		String modename = "enforce";
		
		String ruleName = "PackageToSchema";
		TransformationController.instance.registVariable(getBaseContext(), env.getTransformation().getRule(ruleName));
		Object result = env.getMTVisitor().invokeSemantic(ruleName, null, modename);
		System.out.println("Rule " + ruleName + " has finished execution. Result = " + result);
		
		ruleName = "ClassToTable";
		TransformationController.instance.registVariable(getBaseContext(), env.getTransformation().getRule(ruleName));
		result = env.getMTVisitor().invokeSemantic(ruleName, null, modename);
		System.out.println("Rule " + ruleName + " has finished execution. Result = " + result);
		if (modename.equals("enforce"))
		{
			EMFXMISaver saver = new EMFXMISaver();
			MModelGraph model = env.getModelByTypedModel(env.getTransformation().getFormalParameter("rdbms"));
			saver.save(model, targetModelFile);
		}
	}
	
	private void loadModels()
	{
//		ECoreLoader umlloader = ECoreLoader.getECoreLoader("SimpleUML", "UMLtoRDBMS\\metamodel\\SimpleUML.ecore");
//		TypedModel sourceTypedModel = new TypedModel();
//		ModelType sourceModelType = new ModelType();
//		sourceModelType.setName("SimpleUML");
//		sourceModelType.setMetamodel(umlloader.load());
//		sourceTypedModel.setName("uml");
//		sourceTypedModel.setType(sourceModelType);
//		
//		ECoreLoader rdbmsloader = ECoreLoader.getECoreLoader("SimpleRDBMS", "UMLtoRDBMS\\metamodel\\SimpleRDBMS.ecore");
//		TypedModel targetTypedModel = new TypedModel();
//		ModelType targetModelType = new ModelType();
//		targetModelType.setName("SimpleRDBMS");
//		targetModelType.setMetamodel(rdbmsloader.load());
//		targetTypedModel.setName("rdbms");
//		targetTypedModel.setType(targetModelType);
		
		TypedModel sourceTypedModel = env.getTransformation().getFormalParameter("uml");
		TypedModel targetTypedModel = env.getTransformation().getFormalParameter("rdbms");
		
		EMFXMILoader srcloader = new EMFXMILoader("SimpleUML");
		MModelGraph sourceModel = srcloader.load("UMLtoRDBMS\\model\\SimpleUML.xmi");
		if (sourceModel == null)
			System.out.println("loading source model failed");
		sourceModel.setTypeGraph(sourceTypedModel.getType().getMetamodel());
		
		EMFXMILoader tarloader = new EMFXMILoader("SimpleRDBMS");
		targetModelFile = "UMLtoRDBMS\\model\\SimpleRDBMS.xmi";
		MModelGraph targetModel = tarloader.load(targetModelFile);
		if (targetModel == null)
		{
			System.out.println("loading target model failed");
			targetModel = new MModelGraph();
		}
		targetModel.setTypeGraph(targetTypedModel.getType().getMetamodel());
		
//		List<TypedModel> tms = new ArrayList<TypedModel>();
//		tms.add(sourceTypedModel);
//		tms.add(targetTypedModel);
//		env.getTransformation().setFormalParameters(tms);
		
		env.addTargetmodel(targetTypedModel);
		env.registerModel(sourceTypedModel, sourceModel);
		env.registerModel(targetTypedModel, targetModel);
	}
	
	private void createKeys()
	{
		Map<MetaClass, Key> keys = env.getTransformation().getKeys();
		
		Key TableKey = new Key();
		TableKey.setTypeName("RdbmsTable");
		String[] TableKeyFactors = new String[2];
		TableKeyFactors[0] = "rdbmsName";
		TableKeyFactors[1] = "rdbmsSchema";
		TableKey.setFactors(TableKeyFactors);
		Key SKey = new Key();
		SKey.setTypeName("RdbmsSchema");
		SKey.setFactors(new String[]{"rdbmsName"});
		env.getTransformation().addKey(env.getTransformation().getFormalParameters().get(1).getType().getMetamodel().getMetaClass("RdbmsTable"), TableKey);
		env.getTransformation().addKey(env.getTransformation().getFormalParameters().get(1).getType().getMetamodel().getMetaClass("RdbmsSchema"), SKey);
	}
	
	private void createRules()
	{
		//What is a rule made of: name, variables, domains
		String exp;
		TypedModel srctm = env.getTransformation().getFormalParameter("uml");
		TypedModel tartm = env.getTransformation().getFormalParameter("rdbms");
		
		Rule p2s = new Rule();
		p2s.setName("PackageToSchema");
		p2s.declareVariable("pn", PrimitiveType.TYPE_STRING, null);
		PatternDomain p2ssp = new PatternDomain();
		p2ssp.setFormalParameter(srctm);
		TPatternGraph p2ssg = new TPatternGraph();
		TPatternNode p2ssnp = new TPatternNode();
		p2ssnp.setName("p");
		p2ssnp.setType(srctm.getType().getMetamodel().getMetaClass("UmlPackage"));
		p2ssg.addNode(p2ssnp);
		p2ssp.setPattern(p2ssg);
		exp = "p.umlName=pn;";
		edu.pku.sei.queryscript.parser.Ast.Ast astpn = ScriptParser.parse(exp);
		p2ssp.addExpression(astpn);
		p2s.addPatternDomain(p2ssp);
		
		PatternDomain p2stp = new PatternDomain();
		p2stp.setFormalParameter(tartm);
		p2stp.addDescriptor("check");
		p2stp.addDescriptor("enforce");
		TPatternGraph p2stg = new TPatternGraph();
		TPatternNode p2stns = new TPatternNode();
		p2stns.setName("s");
		p2stns.setType(tartm.getType().getMetamodel().getMetaClass("RdbmsSchema"));
		p2stg.addNode(p2stns);
		p2stp.setPattern(p2stg);
		exp = "s.rdbmsName=pn+'_abc';";
		edu.pku.sei.queryscript.parser.Ast.Ast asttn = ScriptParser.parse(exp);
		p2stp.addExpression(asttn);
		p2s.addPatternDomain(p2stp);
		
		env.registRule("PackageToSchema", p2s);
		env.getTransformation().addRule(p2s);
		
		
		Rule class2table = new Rule();
		
		
		class2table.setName("ClassToTable");
		class2table.declareVariable("cn", PrimitiveType.TYPE_STRING, null);
		
		PatternDomain srcpd = new PatternDomain();
		srcpd.setFormalParameter(srctm);
		TPatternGraph pattern_s = new TPatternGraph();
		
		//adding nodes and edges
		TPatternNode c = new TPatternNode();
		c.setName("c");
		c.setType(srctm.getType().getMetamodel().getMetaClass("UmlClass"));
		pattern_s.addNode(c);
		
		TPatternNode p = new TPatternNode();
		p.setName("p");
		p.setType(srctm.getType().getMetamodel().getMetaClass("UmlPackage"));
		pattern_s.addNode(p);
		
		TPatternEdge c2p = new TPatternEdge();
		MetaClass umlpackage = srctm.getType().getMetamodel().getMetaClass("UmlPackage");
		MetaClass umlpackageelement = srctm.getType().getMetamodel().getMetaClass("UmlPackageElement");
		MetaReference umlnamespace = srctm.getType().getMetamodel().getMetaReference("umlNamespace", umlpackageelement, umlpackage);
		c2p.setType(umlnamespace);
		c2p.setSource(c);
		c2p.setTarget(p);
		pattern_s.addEdge(c2p);
		
		srcpd.setPattern(pattern_s);
		
		exp = "c.umlName=cn;c.umlKind='Persistent';";
		edu.pku.sei.queryscript.parser.Ast.Ast ast2 = ScriptParser.parse(exp);
		srcpd.addExpression(ast2);
		class2table.addPatternDomain(srcpd);
		
		PatternDomain tarpd = new PatternDomain();
		tarpd.addDescriptor("check");
		tarpd.addDescriptor("enforce");
		tarpd.setFormalParameter(tartm);
		TPatternGraph pattern_t = new TPatternGraph();
		
		TPatternNode t = new TPatternNode();
		t.setName("t");
		t.setType(tartm.getType().getMetamodel().getMetaClass("RdbmsTable"));
		pattern_t.addNode(t);
		
		TPatternNode s = new TPatternNode();
		s.setName("s");
		s.setType(tartm.getType().getMetamodel().getMetaClass("RdbmsSchema"));
		pattern_t.addNode(s);
		
		TPatternNode  cl = new TPatternNode();
		cl.setName("cl");
		cl.setType(tartm.getType().getMetamodel().getMetaClass("RdbmsColumn"));
		pattern_t.addNode(cl);
		
		TPatternNode  k = new TPatternNode();
		k.setName("k");
		k.setType(tartm.getType().getMetamodel().getMetaClass("RdbmsKey"));
		pattern_t.addNode(k);
		
		TPatternEdge t2s = new TPatternEdge();
		t2s.setType(tartm.getType().getMetamodel().getMetaReference("rdbmsSchema", t.getType(), s.getType()));
		t2s.setSource(t);
		t2s.setTarget(s);
		pattern_t.addEdge(t2s);
		
		TPatternEdge t2cl = new TPatternEdge();
		t2cl.setType(tartm.getType().getMetamodel().getMetaReference("rdbmsColumn", t.getType(), cl.getType()));
		t2cl.setSource(t);
		t2cl.setTarget(cl);
		pattern_t.addEdge(t2cl);
		
		TPatternEdge t2k = new TPatternEdge();
		t2k.setType(tartm.getType().getMetamodel().getMetaReference("rdbmsKey", t.getType(), k.getType()));
		t2k.setSource(t);
		t2k.setTarget(k);
		pattern_t.addEdge(t2k);
		
		TPatternEdge k2cl = new TPatternEdge();
		k2cl.setType(tartm.getType().getMetamodel().getMetaReference("rdbmsColumn", k.getType(), cl.getType()));
		k2cl.setSource(k);
		k2cl.setTarget(cl);
		pattern_t.addEdge(k2cl);
		
		tarpd.setPattern(pattern_t);
		
		exp = "t.rdbmsName=cn;";
		edu.pku.sei.queryscript.parser.Ast.Ast ast3 = ScriptParser.parse(exp);
		tarpd.addExpression(ast3);
		exp = "cl.rdbmsName=cn+'_tid';";
		edu.pku.sei.queryscript.parser.Ast.Ast ast4 = ScriptParser.parse(exp);
		tarpd.addExpression(ast4);
		exp = "k.rdbmsName=cn+'_pk';";
		edu.pku.sei.queryscript.parser.Ast.Ast ast5 = ScriptParser.parse(exp);
		tarpd.addExpression(ast5);
//		exp = "s.rdbmsName='myPackage';";
//		edu.pku.sei.queryscript.parser.Ast.Ast ast6 = ScriptParser.parse(exp);
//		tarpd.addExpression(ast6);
		
		
		exp = "PackageToSchema(p,s);";
		edu.pku.sei.queryscript.parser.Ast.Ast ast6 = ScriptParser.parse(exp);
		class2table.getPreClause().addExpression(ast6);
		
		class2table.addPatternDomain(tarpd);
		env.registRule("ClassToTable", class2table);
		env.getTransformation().addRule(class2table);
		
	}
}
